/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package main;

import java.util.List;
import tree.*;
import tree.BinaryTree.Operator;

/**
 *
 * @author Ej-Dý
 */
public class SemanticAnalyzer implements TreeVisitor {

    /**
     * Aktualni hloubka zasobniku.
     */
    private int depth;

    /**
     * Maximalni hloubka zasobniku.
     */
    
    private int maDepth[]=new int[ParserAnalyzer.getFTS().size()+1];
    
    private int maxDepth;
    /**
     * Pocet nalezenych chyb.
     */
    private int errorCount;

    /**
     * Vypise chybu.
     */
    private void error(Tree t, String msg) {
        errorCount++;
        System.out.printf("%s%s: %s%n", t.getStart(), t.getEnd(), msg);

    }

    /**
     * Vlozi hodnotu na zasobnik.
     */
    private void push(Type t) {
        depth += t.getSize();
        if (depth > maxDepth) {
            maxDepth = depth;
        }
    }

    /**
     * Vybere hodnotu ze zasobniku.
     */
    private void pop(Type t) {
        depth -= t.getSize();
    }
    public void visitBinary(BinaryTree t) {
        VyrazTree e1 = t.getLeftOperand();
        e1.accept(this);
        Type t1 = e1.getType();

        VyrazTree e2 = t.getRightOperand();
        e2.accept(this);
        Type t2 = e2.getType();

        //if (t1 != t2) {
          //  error(t, "operands must be of the same type");
       // }
        pop(t1);
        pop(t2);
        switch (t.getOperator()) {
            case PLUS:
            case MINUS:
            case KRAT:
            case DELENO:
                if (t1 == Type.INTEGER) {
                    push(Type.INTEGER);
                } else {
                    push(Type.REAL);
                }
                break;

            case VETSI:
            case MENSI:
            case MENSIROV:
            case VETSIROV:
            case ROVNO:
            case NEROVNO:
        }
    }




    public void visitBlock(BlockTree t){
         for (FunTeloTree s : t.getFunTelo()) {
            s.accept(this);
        }
        
         for (VlastniProgramTree s : t.getVlastniProgram()) {
            s.accept(this);
        }
         t.setStackDepth(maxDepth);

         

    }

    public void visitFor(CastForTree t){
        PrirazeniTree e1 = t.getVyraz1();
        e1.accept(this);

        PrirazeniTree e2 = t.getVyraz2();
        e2.accept(this);

        BinaryTree p = t.getPorovnani();

        switch (p.getOperator()) {
            case PLUS:
            case MINUS:
            case KRAT:
            case DELENO:
                error(t, "excepted porovnani");
                break;

            case VETSI:
            case MENSI:
            case MENSIROV:
            case VETSIROV:
            case ROVNO:
            case NEROVNO:
        }

        for( VlastniProgramTree vt : t.getBody()){
        vt.accept(this);
        }
    }

   

    public void visitIdentifier(IdentifierTree t){
        if (!t.isLeftValue()) {
            push(t.getType());
        }

    }

    public void visitIf(CastIfTree t){
        BinaryTree c = t.getPorovnani();
        c.accept(this);
        switch (c.getOperator()) {
            case PLUS:
            case MINUS:
            case KRAT:
            case DELENO:
                error(t, "excepted porovnani");
                break;

            case VETSI:
            case MENSI:
            case MENSIROV:
            case VETSIROV:
            case ROVNO:
            case NEROVNO:

                }

        for (VlastniProgramTree s : t.getCastIf()) {
            s.accept(this);
        }
        
        List<VlastniProgramTree> s2 = t.getCastElse();
        if (s2 != null) {
            for (VlastniProgramTree s : s2) {
            s.accept(this);
        }
        }

    }

   public  void visitLiteral(LiteralTree t){
       push(t.getType());

    }

    public void visitProgram(ProgramTree t){

        BlockTree b = t.getBlock();
        b.accept(this);
        if (errorCount > 0) {
            throw new SemanticException();
        }

    }

    public void visitVariableFun(VariableFunTree t){

    }

    public void visitVariable(VariableTree t){

    }

  

    public void visitPrirazeni(PrirazeniTree t){
        IdentifierTree i = t.getIdentifier();
        i.accept(this);
        VyrazTree e = t.getExpression();
        e.accept(this);
        //if (i.getType() != e.getType()) {
         //   error(e, "cannot assign " + e.getType() + " to " + i.getType());
       // }
        pop(i.getType());

    }

    public void visitAgrument2(Argument2Tree t){
         for (VyrazTree s : t.getVyraz()) {
            s.accept(this);
            
        }

    }

    public void visitArgument(ArgumentTree t){

    }

  
    public void visitCastVyraz(CastVyrazTree t){


    }

    public void visitFunTelo(FunTeloTree t){
        
        for (VlastniProgramTree s : t.getBody()) {
            s.accept(this);
        }

        VyrazTree v=t.getReturn();
        v.accept(this);
        
        t.setStackDepth(maxDepth);
        
        depth=0;
        maxDepth=0;

    }

    public void visitIdentifierFun(IdentifierFunTree t) {
        if (!t.isLeftValue()) {
            push(t.getType());
        }
        Argument2Tree s = t.getArg();
            s.accept(this);
        
    }

    public void visitWriteln(WriteTree t) {
        push(Type.INTEGER);
        VyrazTree e = t.getExpression();
        e.accept(this);
        //push(e.getType());
        pop(Type.INTEGER);
    }

    
    public void visitAlloc(AllocTree at) {
        push(Type.INTEGER);
        at.getVyrazTree().accept(this);
        pop(Type.INTEGER);
    }

    public void visitDimension(DimensionTree dt) {
        push(Type.INTEGER);
        dt.getDalsiDimenze().accept(this);
        dt.getdSize().accept(this);
        pop(Type.INTEGER);
    }

    public void visitReadFileTree(ReadFileTree aThis) {
        //push(Type.INTEGER);
        aThis.getWhere().accept(this);
    }

    public void visitArrayTree(ArrayTree at) {
        //push(Type.INTEGER);
        at.getPoint().accept(this);
    }

    
}